/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "Precompiled.h"
#include "globaldata.h"


#include "i_system.h"
#include "z_zone.h"
#include "m_random.h"

#include "doomdef.h"
#include "p_local.h"

#include "s_sound.h"

// State.
#include "doomstat.h"
#include "r_state.h"

// Data.
#include "sounds.h"





//
// Move a plat up and down
//
void T_PlatRaise( plat_t* plat )
{
	result_e	res;

	switch( plat->status )
	{
		case up:
			res = T_MovePlane( plat->sector,
							   plat->speed,
							   plat->high,
							   plat->crush, 0, 1 );

			if( plat->type == raiseAndChange
					|| plat->type == raiseToNearestAndChange )
			{
				if( !( ::g->leveltime & 7 ) )
					S_StartSound( &plat->sector->soundorg,
								  sfx_stnmov );
			}


			if( res == crushed && ( !plat->crush ) )
			{
				plat->count = plat->wait;
				plat->status = down;
				S_StartSound( &plat->sector->soundorg,
							  sfx_pstart );
			}
			else
			{
				if( res == pastdest )
				{
					plat->count = plat->wait;
					plat->status = waiting;
					S_StartSound( &plat->sector->soundorg,
								  sfx_pstop );

					switch( plat->type )
					{
						case blazeDWUS:
						case downWaitUpStay:
							P_RemoveActivePlat( plat );
							break;

						case raiseAndChange:
						case raiseToNearestAndChange:
							P_RemoveActivePlat( plat );
							break;

						default:
							break;
					}
				}
			}
			break;

		case	down:
			res = T_MovePlane( plat->sector, plat->speed, plat->low, false, 0, -1 );

			if( res == pastdest )
			{
				plat->count = plat->wait;
				plat->status = waiting;
				S_StartSound( &plat->sector->soundorg, sfx_pstop );
			}
			break;

		case	waiting:
			if( !--plat->count )
			{
				if( plat->sector->floorheight == plat->low )
				{
					plat->status = up;
				}
				else
				{
					plat->status = down;
				}
				S_StartSound( &plat->sector->soundorg, sfx_pstart );
			}
		case	in_stasis:
			break;
	}
}


//
// Do Platforms
//  "amount" is only used for SOME platforms.
//
int
EV_DoPlat
( line_t*	line,
  plattype_e	type,
  int		amount )
{
	plat_t*	plat;
	int		secnum;
	int		rtn;
	sector_t*	sec;

	secnum = -1;
	rtn = 0;


	//	Activate all <type> plats that are in_stasis
	switch( type )
	{
		case perpetualRaise:
			P_ActivateInStasis( line->tag );
			break;

		default:
			break;
	}

	while( ( secnum = P_FindSectorFromLineTag( line, secnum ) ) >= 0 )
	{
		sec = &::g->sectors[secnum];

		if( sec->specialdata )
		{
			continue;
		}

		// Find lowest & highest floors around sector
		rtn = 1;
		plat = ( plat_t* )DoomLib::Z_Malloc( sizeof( *plat ), PU_LEVEL, 0 );
		P_AddThinker( &plat->thinker );

		plat->type = type;
		plat->sector = sec;
		plat->sector->specialdata = plat;
		plat->thinker.function.acp1 = ( actionf_p1 ) T_PlatRaise;
		plat->crush = false;
		plat->tag = line->tag;

		switch( type )
		{
			case raiseToNearestAndChange:
				plat->speed = PLATSPEED / 2;
				sec->floorpic = ::g->sides[line->sidenum[0]].sector->floorpic;
				plat->high = P_FindNextHighestFloor( sec, sec->floorheight );
				plat->wait = 0;
				plat->status = up;
				// NO MORE DAMAGE, IF APPLICABLE
				sec->special = 0;

				S_StartSound( &sec->soundorg, sfx_stnmov );
				break;

			case raiseAndChange:
				plat->speed = PLATSPEED / 2;
				sec->floorpic = ::g->sides[line->sidenum[0]].sector->floorpic;
				plat->high = sec->floorheight + amount * FRACUNIT;
				plat->wait = 0;
				plat->status = up;

				S_StartSound( &sec->soundorg, sfx_stnmov );
				break;

			case downWaitUpStay:
				plat->speed = PLATSPEED * 4;
				plat->low = P_FindLowestFloorSurrounding( sec );

				if( plat->low > sec->floorheight )
				{
					plat->low = sec->floorheight;
				}

				plat->high = sec->floorheight;
				plat->wait = TICRATE * PLATWAIT;
				plat->status = down;
				S_StartSound( &sec->soundorg, sfx_pstart );
				break;

			case blazeDWUS:
				plat->speed = PLATSPEED * 8;
				plat->low = P_FindLowestFloorSurrounding( sec );

				if( plat->low > sec->floorheight )
				{
					plat->low = sec->floorheight;
				}

				plat->high = sec->floorheight;
				plat->wait = TICRATE * PLATWAIT;
				plat->status = down;
				S_StartSound( &sec->soundorg, sfx_pstart );
				break;

			case perpetualRaise:
				plat->speed = PLATSPEED;
				plat->low = P_FindLowestFloorSurrounding( sec );

				if( plat->low > sec->floorheight )
				{
					plat->low = sec->floorheight;
				}

				plat->high = P_FindHighestFloorSurrounding( sec );

				if( plat->high < sec->floorheight )
				{
					plat->high = sec->floorheight;
				}

				plat->wait = TICRATE * PLATWAIT;
				plat->status = ( plat_e )( P_Random() & 1 );

				S_StartSound( &sec->soundorg, sfx_pstart );
				break;
		}
		P_AddActivePlat( plat );
	}
	return rtn;
}



void P_ActivateInStasis( int tag )
{
	int		i;

	for( i = 0; i < MAXPLATS; i++ )
		if( ::g->activeplats[i]
				&& ( ::g->activeplats[i] )->tag == tag
				&& ( ::g->activeplats[i] )->status == in_stasis )
		{
			( ::g->activeplats[i] )->status = ( ::g->activeplats[i] )->oldstatus;
			( ::g->activeplats[i] )->thinker.function.acp1
				= ( actionf_p1 ) T_PlatRaise;
		}
}

void EV_StopPlat( line_t* line )
{
	int		j;

	for( j = 0; j < MAXPLATS; j++ )
		if( ::g->activeplats[j]
				&& ( ( ::g->activeplats[j] )->status != in_stasis )
				&& ( ( ::g->activeplats[j] )->tag == line->tag ) )
		{
			( ::g->activeplats[j] )->oldstatus = ( ::g->activeplats[j] )->status;
			( ::g->activeplats[j] )->status = in_stasis;
			( ::g->activeplats[j] )->thinker.function.acv = ( actionf_v )NULL;
		}
}

void P_AddActivePlat( plat_t* plat )
{
	int		i;

	for( i = 0; i < MAXPLATS; i++ )
		if( ::g->activeplats[i] == NULL )
		{
			::g->activeplats[i] = plat;
			return;
		}
	I_Error( "P_AddActivePlat: no more plats!" );
}

void P_RemoveActivePlat( plat_t* plat )
{
	int		i;
	for( i = 0; i < MAXPLATS; i++ )
		if( plat == ::g->activeplats[i] )
		{
			( ::g->activeplats[i] )->sector->specialdata = NULL;
			P_RemoveThinker( &( ::g->activeplats[i] )->thinker );
			::g->activeplats[i] = NULL;

			return;
		}
	I_Error( "P_RemoveActivePlat: can't find plat!" );
}

